package edu.iscas.appcheckclient.service;

import android.app.AlertDialog;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.preference.PreferenceManager;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

import edu.iscas.appcheckclient.Configuration;
import edu.iscas.appcheckclient.R;
import edu.iscas.appcheckclient.activity.AppListActivity;
import edu.iscas.appcheckclient.activity.ResultsActivity;
import edu.iscas.appcheckclient.service.check.CheckMenuListBuilder;


public class FloatingWindowService extends Service implements View.OnClickListener{

    private Context applicationContext;
    private WindowManager windowManager;
    private LayoutInflater layoutInflater;
    private BroadcastReceiver broadcastReceiver;
    private FloatingWindowService floatingWindowService;
    /**
     * the node to be check
     */
    private AccessibilityNodeInfo targetNodeInfo;

    private static View menuView;
    private static ImageButton floatingButton;
    private static ImageButton checkingFloatingButton;
    private static View backpaneView;
    private static View highlightRectView;
    private static View hintView;
    private static Button cancelCheckButton;
    private static TextView hintTextView;
    private static View checkOptionsView;
    private static LinearLayout checkOptionsLayout;

    private int displayMetricHeight;
    private int displayMetricWidth;

    private Boolean isBtnOn;
    private static long thresholdTime = 200L;
    private static final String CONFIG_CHANGED = "android.intent.action.CONFIGURATION_CHANGED";

    private static final String TAG = FloatingWindowService.class.getSimpleName();

    @Override
    public void onClick(View v) {
        setViewInvisible(floatingButton);
        setViewInvisible(backpaneView);
        RecordService recordService = RecordService.getRecordService();
        switch (v.getId()){
            case R.id.rl_appcheck_menu:
                setViewInvisible(menuView);
                setViewVisible(floatingButton);
                break;
            case R.id.tr_add_check:
                setCheckViewsVisible();
                hintTextView.setText(getResources().getString(R.string.hint_add_check));
                setViewInvisible(menuView);
                setViewVisible(floatingButton);
                break;
            case R.id.tr_add_comment:
                setViewInvisible(menuView);
                setViewVisible(floatingButton);
                break;
            case R.id.tr_add_screenshot:
                recordService.addScreenshotAction();
                Toast.makeText(this, getResources().getString(R.string.hint_screenshot), Toast.LENGTH_LONG).show();
                setViewInvisible(menuView);
                setViewVisible(floatingButton);
                break;
            case R.id.tr_finish:
                //这里获取了一下首选项
                //启动resultActivity
                Intent intent = new Intent(this, ResultsActivity.class);
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
                Toast.makeText(this,RecordService.createRequest().toJSON(),Toast.LENGTH_SHORT).show();
                intent.putExtra("request", RecordService.createRequest().toJSON());
                intent.putExtra("uploadable", true);
                startActivity(intent);

                RecordService.pauseRecording();
                setViewInvisible(menuView);
                setViewVisible(floatingButton);
//                setAllViewsInvisible();
//                Intent mainIntent = new Intent(this, AppListActivity.class);
//                mainIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
//                stopServiceAndStartActivity(mainIntent);
//                RecordService.stopRecording();
                break;
            case R.id.tr_stop_recording:
                setAllViewsInvisible();
                AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this.applicationContext);
                alertDialogBuilder.setMessage(R.string.dialog_terminate_message)
                        .setPositiveButton(R.string.dialog_terminate_ok, new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                //启动applist activity
                                Intent intent = new Intent(applicationContext, AppListActivity.class);
                                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
                                startActivity(intent);
                                RecordService.stopRecording();
                                Toast.makeText(getApplicationContext(), "stop recording", Toast.LENGTH_SHORT).show();
                            }
                        })
                        .setNegativeButton(R.string.dialog_terminate_cancel, new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {

                            }
                        });
                AlertDialog alertDialog = alertDialogBuilder.create();
                alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
                alertDialog.show();
                break;
            case R.id.rl_appcheck_backpane:
                break;
        }

    }

    public static void setAllViewsInvisible(){
        setViewInvisible(menuView);
        setViewInvisible(backpaneView);
        setViewInvisible(floatingButton);
        setViewInvisible(highlightRectView);
        setViewInvisible(hintView);
        setViewInvisible(checkOptionsView);
        setViewInvisible(checkingFloatingButton);
    }

    public static void setCheckViewsVisible(){
        setAllViewsInvisible();
        setViewVisible(backpaneView);
        setViewVisible(highlightRectView);
        setViewVisible(hintView);
        setViewVisible(checkingFloatingButton);
    }

    private static class MessengerHandler extends Handler{
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case 1:
                    Log.i(TAG, "receive msg from client: " + msg.getData().getParcelable("action"));
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    private final Messenger mMessenger = new Messenger(new MessengerHandler());


    @Override
    public void onCreate() {
        super.onCreate();
        this.applicationContext = getApplicationContext();
        windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
        layoutInflater = (LayoutInflater)getSystemService(LAYOUT_INFLATER_SERVICE);

        initDisplayMetrics();
        Configuration.sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
        initMenuLayout();
        initCheckingViews();
        initFloatingButton();
        initCheckingFloatingButton();
        initCheckOptionsView();

        this.broadcastReceiver = new FloatingServiceReceiver();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(CONFIG_CHANGED);
        this.applicationContext.registerReceiver(this.broadcastReceiver, intentFilter);
        floatingWindowService = this;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if(floatingButton != null){
            this.windowManager.removeView(floatingButton);
        }
        this.applicationContext.unregisterReceiver(this.broadcastReceiver);
        this.floatingWindowService = null;
    }

    public FloatingWindowService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        //throw new UnsupportedOperationException("Not yet implemented");
        return mMessenger.getBinder();
    }

    public static void setViewInvisible(View view){
        view.setVisibility(View.INVISIBLE);
    }

    public static void setViewVisible(View view){
        view.setVisibility(View.VISIBLE);
    }

    private void setHighlightToTargetNode(AccessibilityNodeInfo nodeInfo){
        if(targetNodeInfo != nodeInfo){
            this.targetNodeInfo = nodeInfo;
            WindowManager.LayoutParams layoutParams = createDefaultLayoutParams();
            Rect rect = new Rect();
            nodeInfo.getBoundsInScreen(rect);
            layoutParams.x = rect.left;
            //这里rect的top和layoutParams不能直接赋值，要减去状态栏的高度
            layoutParams.y = rect.top - getStatusBarHeight();
            layoutParams.width = rect.right - rect.left;
            layoutParams.height = rect.bottom - rect.top;
            this.windowManager.updateViewLayout(highlightRectView, layoutParams);
            String str = "Node Info: " + nodeInfo.getClassName() + ", " + nodeInfo.getViewIdResourceName() + ", " + String.format("position={%s}", new Object[] { rect.flattenToString() });
            hintTextView.setText(str);
        }
    }

    /**
     * set the position of the HintTextView. true to top, false to bottom.
     * @param toTop to the top or bottom
     */
    private void setHintTextViewPosition(boolean toTop){
        WindowManager.LayoutParams layoutParams = createDefaultLayoutParams();
        layoutParams.height = 250;
        layoutParams.y = toTop ? 0 : displayMetricHeight - 250;
        this.windowManager.updateViewLayout(hintView, layoutParams);
    }

    private void initDisplayMetrics(){
        DisplayMetrics displayMetrics = new DisplayMetrics();
        this.windowManager.getDefaultDisplay().getMetrics(displayMetrics);
        this.displayMetricHeight = displayMetrics.heightPixels - getStatusBarHeight();
        this.displayMetricWidth = displayMetrics.widthPixels;
    }


    private void initMenuLayout(){
        menuView = this.layoutInflater.inflate(R.layout.floating_window_menu, null, false);
        setViewInvisible(menuView);
        WindowManager.LayoutParams params = createDefaultLayoutParams();

        menuView.findViewById(R.id.rl_appcheck_menu).setOnClickListener(this);
        menuView.findViewById(R.id.tr_add_check).setOnClickListener(this);
        menuView.findViewById(R.id.tr_add_comment).setOnClickListener(this);
        menuView.findViewById(R.id.tr_add_screenshot).setOnClickListener(this);
        menuView.findViewById(R.id.tr_finish).setOnClickListener(this);
        menuView.findViewById(R.id.tr_stop_recording).setOnClickListener(this);

        this.windowManager.addView(menuView, params);
    }

    private void initFloatingButton(){
        floatingButton = new ImageButton(this);
        floatingButton.setBackgroundResource(R.drawable.ic_add_circle_48dp);
        //
        final WindowManager.LayoutParams layoutParams = createFloatingButtonParams();

        this.windowManager.addView(floatingButton, layoutParams);

        floatingButton.setOnTouchListener(new View.OnTouchListener() {
            private long startTime;
            private int initialX;
            private int initialY;
            private float initialTouchX;
            private float initialTouchY;

            private void openMenu() {
                floatingButton.setVisibility(View.INVISIBLE);
                setViewVisible(menuView);
            }

            private void setBtnState(boolean paramBoolean) {
                isBtnOn = paramBoolean;
                if (paramBoolean) {
                    this.startTime = System.currentTimeMillis();
                }
            }

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        setBtnState(true);
                        initialX = layoutParams.x;
                        initialY = layoutParams.y;
                        initialTouchX = event.getRawX();
                        initialTouchY = event.getRawY();
                        break;
                    case MotionEvent.ACTION_UP:
                        if (isBtnOn) {
                            setBtnState(false);
                            //视为没有拖动，并点击
                            if (System.currentTimeMillis() - this.startTime < thresholdTime) {
                                layoutParams.x = initialX;
                                layoutParams.y = initialY;
                                openMenu();
                            }
                            if (layoutParams.x >= displayMetricWidth / 2) {
                                layoutParams.x = displayMetricWidth - 128;
                            } else {
                                layoutParams.x = 0;
                            }
                            windowManager.updateViewLayout(floatingButton, layoutParams);
                        }
                        break;
                    case MotionEvent.ACTION_MOVE:
                        if (isBtnOn) {
                            layoutParams.x = initialX + (int) (event.getRawX() - initialTouchX);
                            layoutParams.y = initialY + (int) (event.getRawY() - initialTouchY);
                            windowManager.updateViewLayout(floatingButton, layoutParams);
                        }
                        break;
                }
                return false;
            }

        });

    }

    private void initCheckingViews(){
        backpaneView = layoutInflater.inflate(R.layout.layout_backpane, null, false);
        setViewInvisible(backpaneView);
        backpaneView.findViewById(R.id.rl_appcheck_backpane).setOnClickListener(this);
        this.windowManager.addView(backpaneView, createDefaultLayoutParams());

        highlightRectView = new RelativeLayout(this);
        highlightRectView.setBackgroundResource(R.drawable.highlight_rect);
        WindowManager.LayoutParams params = createDefaultLayoutParams();
        params.height = 0;
        params.width = 0;
        this.windowManager.addView(highlightRectView, params);

        hintView = layoutInflater.inflate(R.layout.layout_hint, null, false);
        hintTextView = (TextView)hintView.findViewById(R.id.tv_hintText);
        cancelCheckButton = (Button)hintView.findViewById(R.id.btn_cancel_check);
        cancelCheckButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                setAllViewsInvisible();
                setViewVisible(floatingButton);
            }
        });

        setViewInvisible(hintView);
        params = createDefaultLayoutParams();
        params.height = 250;
        params.y = (this.displayMetricHeight - 250);
        this.windowManager.addView(hintView, params);

    }

    private void initCheckingFloatingButton(){
        this.isBtnOn = false;
        checkingFloatingButton = new ImageButton(this);
        checkingFloatingButton.setBackgroundResource(R.drawable.ic_crosshair);
        final WindowManager.LayoutParams layoutParams = createFloatingButtonParams();

        checkingFloatingButton.setOnTouchListener(new View.OnTouchListener() {
            RecordService recordService = RecordService.getRecordService();
            private int initialX;
            private int initialY;
            private float initialTouchX;
            private float initialTouchY;
            private void setBtnState(boolean paramBoolean) {
                isBtnOn = paramBoolean;
            }

            private void locateNode(){
                //应该是加128的一半，这里把图标当成128*128
                int i = layoutParams.x + 64;
                int j = layoutParams.y + 64 + getStatusBarHeight();
                AccessibilityNodeInfo targetNode = recordService.locateNodeInfoByAxis(i, j);
                if (targetNode != null){
                    setHighlightToTargetNode(targetNode);
                }
                if(j > displayMetricHeight - 300){
                    setHintTextViewPosition(true);
                }else
                    setHintTextViewPosition(false);
            }

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()){
                    case MotionEvent.ACTION_DOWN:
                        setBtnState(false);
                        initialX = layoutParams.x;
                        initialY = layoutParams.y;
                        initialTouchX = event.getRawX();
                        initialTouchY = event.getRawY();
                        break;
                    case MotionEvent.ACTION_UP:
                        this.locateNode();
                        if(targetNodeInfo != null){
                            checkOptionsLayout.removeAllViews();
                            setAllViewsInvisible();
                            //构造目标节点的checkMenuList，也就是能够添加的assert的列表，对每项添加button
                            for (String str : CheckMenuListBuilder.buildList(targetNodeInfo)) {
                                Button button = new Button(applicationContext);
                                button.setText(str);
                                button.setOnClickListener(recordService.getCheckMenuListener(targetNodeInfo, checkOptionsView));
                                checkOptionsLayout.addView(button);
                            }
                            setViewVisible(checkOptionsView);
                        }
                        break;
                    case MotionEvent.ACTION_MOVE:
                        setBtnState(true);
                        if(isBtnOn){
                            layoutParams.x = initialX + (int) (event.getRawX() - initialTouchX);
                            layoutParams.y = initialY + (int) (event.getRawY() - initialTouchY);
                            windowManager.updateViewLayout(checkingFloatingButton, layoutParams);
                            locateNode();
                        }
                }
                return false;
            }
        });

        this.windowManager.addView(checkingFloatingButton, layoutParams);
        setViewInvisible(checkingFloatingButton);
    }

    private void initCheckOptionsView(){
        checkOptionsView = this.layoutInflater.inflate(R.layout.layout_check_options, null, false);
        setViewInvisible(checkOptionsView);
        checkOptionsView.findViewById(R.id.rl_appcheck_check).setOnClickListener(this);
        checkOptionsLayout = (LinearLayout)checkOptionsView.findViewById(R.id.ll_appcheck_check_options);
        WindowManager.LayoutParams layoutParams = createDefaultLayoutParams();
        this.windowManager.addView(checkOptionsView, layoutParams);
    }

    private int getStatusBarHeight(){
        int i1 = 0;
        int i2 = getResources().getIdentifier("status_bar_height", "dimen", "android");
        if (i2 > 0) {
            i1 = getResources().getDimensionPixelSize(i2);
        }
        return i1;
    }

    private WindowManager.LayoutParams createFloatingButtonParams(){
        WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.TYPE_PHONE,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                PixelFormat.TRANSLUCENT);
        layoutParams.gravity = Gravity.TOP | Gravity.START;
        layoutParams.x = 0;
        layoutParams.y = 100;
        layoutParams.height = 128;
        layoutParams.width = 128;
        return layoutParams;
    }

    private WindowManager.LayoutParams createDefaultLayoutParams(){
        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.TYPE_PHONE,
                WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                PixelFormat.TRANSLUCENT);
        params.gravity = 8388659;
        params.height = this.displayMetricHeight;
        params.width = this.displayMetricWidth;
        return params;
    }

    public class FloatingServiceReceiver extends BroadcastReceiver{

        private final String TAG = FloatingServiceReceiver.class.getSimpleName();
        @Override
        public void onReceive(Context context, Intent intent) {
            if(intent.getAction().equals(CONFIG_CHANGED)){
                Log.d(TAG, "Broadcast: " + CONFIG_CHANGED);
                initDisplayMetrics();
            }
        }
    }
}
